package com.nulldev.util.java.swing;

import java.awt.Adjustable;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Paint;
import java.awt.Window;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.regex.Pattern;

import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JPopupMenu;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;

import com.nulldev.util.java.JVMUtil;
import com.nulldev.util.java.ReflectionUtils;
import com.nulldev.util.java.swing.SwingUtil.ScrollDirection;
import com.nulldev.util.java.swing.fastEPane.FastEditorKit;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.os.GPUUtil;
import com.nulldev.util.os.GPUUtil.GPU;
import com.nulldev.util.os.OSUtil;
import com.nulldev.util.os.OSUtil.OSType;
import com.nulldev.util.threading.v2.ThreadExecution;
import com.nulldev.util.threading.v2.ThreadManager;
import com.nulldev.util.threading.v2.ThreadPriority;
import com.nulldev.util.threading.v2.nThread;

public class SwingUtil {

	private static final Logger log = LoggerFactory.getLogger();
	private static final Object charsBufferLock = new Object();
	public static final long FPS_240 = (1000L / 240L);
	public static final long FPS_144 = (1000L / 144L);
	public static final long FPS_60 = (1000L / 60L);
	public static final long FPS_30 = (1000L / 30L);
	public static final long FPS_24 = (1000L / 24L);
	
	public static final long fps(final int fps) {
		return (1000L / fps);
	}
	
	private static char[] charsBuffer = new char[100];
	public static void toggleJMenuFix(final boolean state) {
		JPopupMenu.setDefaultLightWeightPopupEnabled(!state);
	}

	public static int stringWidth(final FontMetrics fm, final String string) {
		return stringWidth(null, fm, string);
	}
	
	public static void centerFrame(final Window frame) {
		frame.setLocation((GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getWidth()
				- frame.getWidth()) / 2,
		(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getHeight()
				- frame.getHeight()) / 2);
	}

	public static int stringWidth(JComponent arg, FontMetrics arg0, String arg1) {
		if (arg1 != null && !arg1.equals("")) {
			boolean arg2 = arg != null && arg.getClientProperty(TextAttribute.NUMERIC_SHAPING) != null;
			if (arg2) {
				Object arg3 = charsBufferLock;
				synchronized (charsBufferLock) {
					int arg4 = syncCharsBuffer(arg1);
					arg2 = isComplexLayout(charsBuffer, 0, arg4);
				}
			}

			if (arg2) {
				TextLayout arg7 = createTextLayout(arg, arg1, arg0.getFont(), arg0.getFontRenderContext());
				return (int) arg7.getAdvance();
			} else {
				return arg0.stringWidth(arg1);
			}
		} else {
			return 0;
		}
	}

	public static final boolean isComplexLayout(char[] arg, int arg0, int arg1) {
		return isComplexText(arg, arg0, arg1);
	}

	public static boolean isComplexText(char[] arg, int arg0, int arg1) {
		for (int arg2 = arg0; arg2 < arg1; ++arg2) {
			if (arg[arg2] >= 768 && isNonSimpleChar(arg[arg2])) {
				return true;
			}
		}

		return false;
	}

	public static boolean isNonSimpleChar(char arg) {
		return isComplexCharCode(arg) || arg >= '?' && arg <= '?';
	}

	public static boolean isComplexCharCode(int arg) {
		return arg >= 768 && arg <= 8303
				? (arg <= 879
						? true
						: (arg < 1424
								? false
								: (arg <= 1791
										? true
										: (arg < 2304
												? false
												: (arg <= 3711
														? true
														: (arg < 3840
																? false
																: (arg <= 4095
																		? true
																		: (arg < 4352
																				? false
																				: (arg < 4607
																						? true
																						: (arg < 6016
																								? false
																								: (arg <= 6143
																										? true
																										: (arg < 8204
																												? false
																												: (arg <= 8205
																														? true
																														: (arg >= 8234
																																&& arg <= 8238
																																		? true
																																		: arg >= 8298
																																				&& arg <= 8303))))))))))))))
				: false;
	}

	private static TextLayout createTextLayout(JComponent arg, String arg0, Font arg1, FontRenderContext arg2) {
		Object arg3 = arg == null ? null : arg.getClientProperty(TextAttribute.NUMERIC_SHAPING);
		if (arg3 == null) {
			return new TextLayout(arg0, arg1, arg2);
		} else {
			HashMap arg4 = new HashMap();
			arg4.put(TextAttribute.FONT, arg1);
			arg4.put(TextAttribute.NUMERIC_SHAPING, arg3);
			return new TextLayout(arg0, arg4, arg2);
		}
	}

	private static int syncCharsBuffer(String arg) {
		int arg0 = arg.length();
		if (charsBuffer != null && charsBuffer.length >= arg0) {
			arg.getChars(0, arg0, charsBuffer, 0);
		} else {
			charsBuffer = arg.toCharArray();
		}

		return arg0;
	}

	public static void enableJSO() {
		if (!isX11Safe()) {
			enableHeadless();
		}
		if (OSUtil.getOperatingSystemType() == OSType.Windows) {
			System.setProperty("sun.java2d.transaccel", "true");
			System.setProperty("sun.java2d.d3d", "true");
			System.setProperty("sun.java2d.ddforcevram", "true");
		}else {
			System.setProperty("sun.java2d.opengl", "true");
			System.setProperty("sun.java2d.xrender", "true");
		}
	}

	public static void enableAA() {
		System.setProperty("awt.useSystemAAFontSettings","on");
		System.setProperty("swing.aatext", "true");
	}

	public static void scroll(final JScrollPane scrollPane, final ScrollDirection direction) {
	    final JScrollBar verticalBar = scrollPane.getVerticalScrollBar();
	    // If we want to scroll to the top set this value to the minimum, else to the maximum
	    final int topOrBottom = direction.equals(ScrollDirection.UP) ? verticalBar.getMinimum() : verticalBar.getMaximum();
	    AdjustmentListener scroller = new AdjustmentListener() {
	        @Override
	        public void adjustmentValueChanged(AdjustmentEvent e) {
	            Adjustable adjustable = e.getAdjustable();
	            adjustable.setValue(topOrBottom);
	            // We have to remove the listener since otherwise the user would be unable to scroll afterwards
	            verticalBar.removeAdjustmentListener(this);
	        }
	    };
	    verticalBar.addAdjustmentListener(scroller);
	}

	public enum ScrollDirection {
	    UP, DOWN
	}

	public static void enableJSOF() {
		final GPU gpu = GPUUtil.parse();
		if (OSUtil.getOperatingSystemType() == OSType.Windows && GPUUtil.isD3DReady(gpu, 9)) {
			System.setProperty("sun.java2d.transaccel", "true");
			System.setProperty("sun.java2d.d3d", "true");
			System.setProperty("sun.java2d.ddforcevram", "true");
		}else {
			enableJSO();
			//System.setProperty("sun.java2d.opengl", "true");
			//System.setProperty("sun.java2d.xrender", "true");
		}
	}

	public static void drawFromLeft(final Graphics2D g2d, final int width, final String string, final int y) {
		g2d.drawString(string, width - g2d.getFontMetrics().stringWidth(string) - 5, y);
	}

	public static String getRenderer() {
		if (OSUtil.getOperatingSystemType() == OSType.Windows) {
			return "Direct3D/DirectDraw";
		}else if (OSUtil.getOperatingSystemType() == OSType.MacOS || OSUtil.getOperatingSystemType() == OSType.Linux) {
			return "OpenGL";
		}else {
			return "Software Renderer";
		}
	}
	
	public static void debug_LF() {
		for (final LookAndFeelInfo lf : UIManager.getInstalledLookAndFeels()) {
			System.out.println(lf.toString());
		}
	}

	public static void enableHeadless() {
		System.setProperty("java.awt.headless", "true");
	}

	private static boolean ran, x11Safe;
	public static boolean isX11Safe() {
		if (ran) {
			return x11Safe;
		}else {
			if (OSUtil.getOperatingSystemType() != OSUtil.OSType.Linux
					&& OSUtil.getOperatingSystemType() != OSType.BSD) {
				ran = true;
				return x11Safe = true;
			}
			try {
				final JFrame test_frame = new JFrame("nullUtil 2.1: X11 Test");
				test_frame.setVisible(true);
				Thread.sleep(50L);
				test_frame.setVisible(false);
				test_frame.dispose();
				ran = true;
				return x11Safe = true;
			}catch (Exception e) {
				ran = true;
				return x11Safe = false;
			}
		}
	}
	
	   public static void setOpaque(Window window, boolean opaque) {

	        try {

	            Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
	            if (awtUtilsClass != null) {

	                Method method = awtUtilsClass.getMethod("setWindowOpaque", Window.class, boolean.class);
	                method.invoke(null, window, opaque);
//	                com.sun.awt.AWTUtilities.setWindowOpaque(this, opaque);
//	                ((JComponent) window.getContentPane()).setOpaque(opaque);

	            }

	        } catch (Exception exp) {
	        	try {
	        		final Object o = ReflectionUtils.createInstance("com.sun.awt.AWTUtilities");
		        	ReflectionUtils.invokeFunction(o, "setWindowOpaque", window, opaque);
	        	}catch (Exception exp0) {
	        		com.sun.awt.AWTUtilities.setWindowOpaque(window, opaque);
	        	}
	        }

	    }

	    public static void setOpacity(Window window, float opacity) {

	        try {

	            Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
	            if (awtUtilsClass != null) {

	                Method method = awtUtilsClass.getMethod("setWindowOpacity", Window.class, float.class);
	                method.invoke(null, window, opacity);

	            }

	        } catch (Exception exp) {

	            exp.printStackTrace();

	        }

	    }

	    public static float getOpacity(Window window) {

	        float opacity = 1f;
	        try {

	            Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
	            if (awtUtilsClass != null) {

	                Method method = awtUtilsClass.getMethod("getWindowOpacity", Window.class);
	                Object value = method.invoke(null, window);
	                if (value != null && value instanceof Float) {

	                    opacity = ((Float) value).floatValue();

	                }

	            }

	        } catch (Exception exp) {

	            exp.printStackTrace();

	        }


	        return opacity;
	    }

		public static void addCopyAndPaste(final Component c) {
			c.addKeyListener(new KeyListener() {
				
				@Override
				public void keyTyped(KeyEvent e) {
		            if (e.getKeyCode() == 86 && ((e.getModifiers() & KeyEvent.CTRL_MASK) != 0)) {
		                java.awt.datatransfer.Clipboard clipboard = java.awt.Toolkit.getDefaultToolkit().getSystemClipboard();
		                java.awt.datatransfer.Transferable clipData = clipboard.getContents(clipboard);
		                String s;
		                try {
		                    s = (String)(clipData.getTransferData(java.awt.datatransfer.DataFlavor.stringFlavor));
		                } catch (Exception ex) {
		                    s = ex.toString();
		                }
		                if (c instanceof JTextField) {
		                	final JTextField x = (JTextField) c;
		                	x.setText(s);
		                }
		            }
				}
				
				@Override
				public void keyReleased(KeyEvent e) {
				}
				
				@Override
				public void keyPressed(KeyEvent e) {
				}
			});
		}

		public static void scrollToBottom(final JScrollPane scrollPane) {
		    JScrollBar verticalBar = scrollPane.getVerticalScrollBar();
		    AdjustmentListener downScroller = new AdjustmentListener() {
		        @Override
		        public void adjustmentValueChanged(AdjustmentEvent e) {
		            Adjustable adjustable = e.getAdjustable();
		            adjustable.setValue(adjustable.getMaximum());
		            verticalBar.removeAdjustmentListener(this);
		        }
		    };
		    verticalBar.addAdjustmentListener(downScroller);
		}

		private static final ThreadManager tm = new ThreadManager();
		
		public static void appendText(final JEditorPane textPane,
				final String textIn, final JScrollPane scrollPane) {
//			if (this.textPane.getText() == null || this.textPane.getText().length() == 0) {
//				this.textPane.setText(textIn);
//			}else {
//				this.textPane.setText(this.textPane.getText() + JVMUtil.lineSep() + textIn);
//			}
			final String text = textIn.replaceAll(Pattern.quote("<"), "&#60;").replaceAll(Pattern.quote(">"), "&#62;")
					.replaceAll(JVMUtil.lineSep(), "<br>");
			tm.addTask(new nThread(new Runnable() {

				@Override
				public void run() {
					//System.out.println("task.started()");
					final HTMLDocument doc = (HTMLDocument) textPane.getDocument();
					if (textPane.getEditorKit() instanceof FastEditorKit) {
						//System.out.println("task.type == FastEditorKit");
						final FastEditorKit editorKit = (FastEditorKit) textPane.getEditorKit();
						try {
//							if (lines.moreThan(limit)) {
//								lines = lNumber.NIL;
//								//textPane.setText("");
//							}
							editorKit.insertHTML(doc, doc.getLength(), text, 0, 0, null);
							//System.out.println("task.type == FastEditorKit");
							//lines = lines.add(1);
							SwingUtil.scroll(scrollPane, ScrollDirection.DOWN);
						} catch (Exception e1) {
							try {
								editorKit.insertHTML(doc, doc.getLength(), text, 0, 0, null);
							} catch (Exception e) {
								e1.printStackTrace();
							}
						}
					}else {
						//System.out.println("task.type == HTMLEditorKit");
						final HTMLEditorKit editorKit = (HTMLEditorKit) textPane.getEditorKit();
						try {
//							if (lines.moreThan(limit)) {
//								lines = lNumber.NIL;
//								//textPane.setText("");
//							}
							editorKit.insertHTML(doc, doc.getLength(), text, 0, 0, null);
							//lines = lines.add(1);
							SwingUtil.scroll(scrollPane, ScrollDirection.DOWN);
						} catch (Exception e1) {
							try {
								editorKit.insertHTML(doc, doc.getLength(), text, 0, 0, null);
							} catch (Exception e) {
								e1.printStackTrace();
							}
						}
					}
				}
			}, "$clientTab.chatAdd", ThreadPriority.NORMAL, ThreadExecution.NORMAL));
		}
		
		public static void appendPure(final JEditorPane textPane,
				final String textIn, final JScrollPane scrollPane) {
//			if (this.textPane.getText() == null || this.textPane.getText().length() == 0) {
//				this.textPane.setText(textIn);
//			}else {
//				this.textPane.setText(this.textPane.getText() + JVMUtil.lineSep() + textIn);
//			}
			final String text = textIn.replaceAll(JVMUtil.lineSep(), "<br>");
			//System.out.println("task.started()");
			final HTMLDocument doc = (HTMLDocument) textPane.getDocument();
			if (textPane.getEditorKit() instanceof FastEditorKit) {
				//System.out.println("task.type == FastEditorKit");
				final FastEditorKit editorKit = (FastEditorKit) textPane.getEditorKit();
				try {
//					if (lines.moreThan(limit)) {
//						lines = lNumber.NIL; //lines.minus(1);
//						//textPane.setText("");
//					}
					editorKit.insertHTML(doc, doc.getLength(), text, 0, 0, null);
					//System.out.println("task -> FastEditorKit.dispatch()");
//					lines = lines.add(1);
					SwingUtil.scroll(scrollPane, ScrollDirection.DOWN);
				} catch (Exception e1) {
					try {
						editorKit.insertHTML(doc, doc.getLength(), text, 0, 0, null);
					} catch (Exception e) {
						appendText(textPane, "> Document seems to have corrupted itself, removing it.",
								scrollPane);
						textPane.setDocument(editorKit.createDefaultDocument());
						appendPure(textPane, textIn, scrollPane);
					}
				}
			}else {
				//System.out.println("task.type == HTMLEditorKit");
				final HTMLEditorKit editorKit = (HTMLEditorKit) textPane.getEditorKit();
				try {
//					if (lines.moreThan(limit)) {
//						lines = lNumber.NIL; //lines.minus(1);
//						textPane.setText("");
//					}
					editorKit.insertHTML(doc, doc.getLength(), text, 0, 0, null);
					//System.out.println("task -> HTMLEditorKit.dispatch()");
//					lines = lines.add(1);
					SwingUtil.scroll(scrollPane, ScrollDirection.DOWN);
				} catch (Exception e1) {
					try {
						editorKit.insertHTML(doc, doc.getLength(), text, 0, 0, null);
					} catch (Exception e) {
						e1.printStackTrace();
					}
				}
			}
			if (true) {
				return;
			}
			tm.addTask(new nThread(new Runnable() {

				@Override
				public void run() {
					//System.out.println("task.started()");
					final HTMLDocument doc = (HTMLDocument) textPane.getDocument();
					if (textPane.getEditorKit() instanceof FastEditorKit) {
						//System.out.println("task.type == FastEditorKit");
						final FastEditorKit editorKit = (FastEditorKit) textPane.getEditorKit();
						try {
//							if (lines.moreThan(limit)) {
//								lines = lNumber.NIL; //lines.minus(1);
//								//textPane.setText("");
//							}
							editorKit.insertHTML(doc, doc.getLength(), text, 0, 0, null);
							//System.out.println("task -> FastEditorKit.dispatch()");
//							lines = lines.add(1);
							SwingUtil.scroll(scrollPane, ScrollDirection.DOWN);
						} catch (Exception e1) {
							try {
								editorKit.insertHTML(doc, doc.getLength(), text, 0, 0, null);
							} catch (Exception e) {
								e1.printStackTrace();
							}
						}
					}else {
						//System.out.println("task.type == HTMLEditorKit");
						final HTMLEditorKit editorKit = (HTMLEditorKit) textPane.getEditorKit();
						try {
//							if (lines.moreThan(limit)) {
//								lines = lNumber.NIL; //lines.minus(1);
//								textPane.setText("");
//							}
							editorKit.insertHTML(doc, doc.getLength(), text, 0, 0, null);
							//System.out.println("task -> HTMLEditorKit.dispatch()");
//							lines = lines.add(1);
							SwingUtil.scroll(scrollPane, ScrollDirection.DOWN);
						} catch (Exception e1) {
							try {
								editorKit.insertHTML(doc, doc.getLength(), text, 0, 0, null);
							} catch (Exception e) {
								e1.printStackTrace();
							}
						}
					}
				}
			}, "$clientTab.chatAdd", ThreadPriority.NORMAL, ThreadExecution.NORMAL));
		}

		public static int getScreenWidth() {
			return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getWidth();
		}
		
		public static int getScreenHeight() {
			return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getHeight();
		}
}
